package com.outstanding.ifml.parser;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.ref.SoftReference;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import com.outstanding.ifml.Code;
import com.outstanding.ifml.IFMLPaserProperties;
import com.outstanding.ifml.exception.IFMLException;
import com.outstanding.ifml.warpper.Liner;

import static com.outstanding.ifml.parser.IFMLToken.*;

/**
 * 
 * 
 * @author Chenzx
 *
 */
public class DefaultIFMLLexer implements IFMLLexer {

	private final static ThreadLocal<SoftReference<char[]>> BUF_REF_LOCAL = new ThreadLocal<SoftReference<char[]>>();

	private Reader reader;
	// 分批获取到的内容字符集合
	private char[] buf;
	// 按8192的长度读取上面的字符
	public static int BUF_INIT_LEN = 8192;
	// 实际读取的长度，即buf的实际长度
	private int bufLength;

	// 当前的字符的ascii码
	protected int token;
	// 当前字符
	protected char ch;

	// 前一个字符（为了处理截断而进行的处理）
	protected char lastch;

	// 当前读取的位置,相对整个文本
	protected int bp;
	// 当前读取的位置,相对整个数组
	protected int index = -1;

	/**
	 * A character buffer for literals.
	 */
	protected char[] sbuf;
	protected int sp;

	
	private Liner curLiner;
	
	public void setCurLiner(Liner curLiner) {
		this.curLiner = curLiner;
	}

	private IFMLPaserProperties properties;

	public DefaultIFMLLexer() {
	}

	@Override
	public void init(InputStream input, IFMLPaserProperties properties) {
		this.properties = properties;
		this.reader = new BufferedReader(new InputStreamReader(input, this.properties.getEncoding()));

		SoftReference<char[]> bufRef = BUF_REF_LOCAL.get();
		if (bufRef != null) {
			this.buf = bufRef.get();
			BUF_REF_LOCAL.set(null);
		}

		if (this.buf == null) {
			this.buf = new char[BUF_INIT_LEN];
		}
		if (sbuf == null) {
			sbuf = new char[64];
		}

		try {
			bufLength = reader.read(this.buf);
		} catch (IOException e) {
			throw new IFMLException("1000", e.getMessage());
		}

		bp = -1;

		nextToken();
		if (ch == 65279) {
			nextToken();
		}
	}

	@Override
	public void nextToken() {
		// bp初始值为-1，index从0开始。
		++bp;
		// 当当前的索引 已经大于 读取数组读取大小
		if (index >= bufLength) {
			// 表示文件已经到了最后了
			if (bufLength == -1) {
				ch = EOI;
				return;
			}
			try {
				// 从bp这个位置开始读
				bufLength = reader.read(buf, bp, bufLength);
			} catch (IOException e) {
				throw new IFMLException(Code.T1000, e.getMessage());
			}
			if (bufLength == 0) {
				throw new IFMLException(Code.T1001, "illegal stat, textLength is zero");
			}
			if (bufLength == -1) {
				ch = EOI;
				return;
			}
			// 重新开始计数索引
			index = -1;
			lastch = this.ch;
		}
		index++;
		this.ch = buf[index];
		if(this.curLiner != null)
			this.curLiner.putChar(this.ch);
	}

	// 返回对应字符的ascii码，决定是否往下推进（什么事也不做，往下走一个字符，往上回退一个字符），并返回ascii码
	@Override
	public int token() {
		// 首先判断是否为控制符号
		if (Character.isISOControl(this.ch)) {
			if (this.ch == 0x04 || this.ch == 0x03 || this.ch == 0x0A || this.ch == 0x0D || this.ch == '\r'
					|| this.ch == '\n') {
				// 处理"\r\n"
				if (this.ch == '\r') {
					nextToken();
					if (this.ch != '\n') {
						// 需要回退一步
						lastToken();
					}
				}
				return RETURN;
			}
			if (this.ch == EOI) {
				return EOF;
			}
			if (this.ch == 0) {
				return BLANK;
			}
			// 制表符不认为是空格，后续可以根据配置项：translate_tab_space_size =num进行转换为空格。
			else if (this.ch == 0x09) {
				//制表符的处理
				if (this.properties.isTranslate_tab_space()) {
					return TABBLANK;
				} else {
					throw new IFMLException(Code.T1002, "unsuport the tab symbol in the text");
				}
			} else {
				throw new IFMLException(Code.T1003, "unsuport the symbol in the text");
			}
		} else if (Character.isWhitespace(this.ch)) {
			return BLANK;
		} else if (this.ch == '(') {
			return LPAREN;
		} else if (this.ch == ')') {
			return RPAREN;
		} else if (this.ch == ':') {
			return COLON;
		} else if (this.ch == '#') {
			return COMMENT;
		} else if (this.ch == '[') {
			return LBRACKET;
		} else if (this.ch == ']') {
			return RBRACKET;
		} else if (this.ch == ',') {
			return COMMA;
		}else if (this.ch == '{') {
			return LBRACE;
		}else if (this.ch == '}') {
			return RBRACE;
		}else if (this.ch == '\'') {
			return SINGLEQUOTATION;
		} else if (this.ch == '"') {
			return DOUBLEQUOTATION;
		} else if (this.ch == '\\') {
			// 下一个字符
			nextToken();
			if (this.ch == '"') {
				// 表示遇到了转译
				return ESCAPEDOUBLE;
			}else if(this.ch == '\'') {
				// 表示遇到了转译
				return ESCAPESINGLE;
				
			} else {
				// 需要回退一步
				lastToken();
				return LITERALS;
			}
		} else if (Character.isJavaIdentifierPart(this.ch)) {
			return IDENTIFIER;
		} else {
			return LITERALS;

		}

	}

	// 倒退一步
	private void lastToken() {
		--bp;
		if (index == 0) {
			this.ch = lastch;
			index = -1;
		} else {
			this.ch = buf[--index];
		}
		this.curLiner.lastChar();
	}

	/**
	 * 添加一个character to sbuf.
	 */
	private final void putChar(char ch) {
		if (sp == sbuf.length - 1) {
			char[] newsbuf = new char[sbuf.length * 2];
			System.arraycopy(sbuf, 0, newsbuf, 0, sbuf.length);
			sbuf = newsbuf;
		}
		sbuf[sp++] = ch;
	}

	@Override
	public char getChar() {
		return this.ch;
	}
	
	
	/**
	 * 当遇到注释时，连续解析字符，成为所有
	 */
	@Override
	public String commentVal() {
		StringBuffer sb = new StringBuffer();
		for (;;) {
			nextToken();
			if (token() == RETURN || token() == EOF) {
				return sb.toString();
			}
			sb.append(this.ch);
		}
	}

	// 下一个有效字符,过滤掉空格或制表符
	@Override
	public void readNextToken() {
		for (;;) {
			nextToken();
			if (token() == BLANK || token() == TABBLANK) {
				continue;
			} else {
				break;
			}
		}
	}

	@Override
	public IFMLPaserProperties getProperties() {
		return properties;
	}

	// 返回一个字符的unicode的编码值
	public static String getUnicode(char c) {
		int ch = (int) c;
		String uniCodeTemp = null;
		if (ch > 255)
			uniCodeTemp = "\\u" + Integer.toHexString(ch);
		else
			uniCodeTemp = "\\u00" + Integer.toHexString(ch);
		return uniCodeTemp;
	}

	@Override
	public String identifierVal(char[] c) {
		StringBuffer sb = new StringBuffer();
		if(c != null) {
			sb.append(c);
		}
		for (;;) {
			nextToken();
			if (token() == IDENTIFIER || token() == LITERALS) {
				sb.append(getChar());
				continue;
			} else if(token() == BLANK || token() == TABBLANK ||
					token() == COMMENT ||token() == LPAREN ||
					token() == EOF || token()==COLON  || token()==RBRACE || token()==RETURN){
				lastToken();
				break;
			}else{
				if(c != null)
					throw new IFMLException(Code.T1004,"获取变量"+new String(c)+token());
				else
					throw new IFMLException(Code.T1005,"获取变量"+token());
			}
		}
		return sb.toString();
	}

	@Override
	public String identifierVal(char c) {
		char[] arrayChar = new char[1]; 
		arrayChar[0] = c;
		return identifierVal(arrayChar);
	}

	@Override
	public String doubleQuotationStringVal() {
		StringBuffer sb = new StringBuffer();
		for (;;) {
			nextToken();
			if (token() != DOUBLEQUOTATION) {
				sb.append(getChar());
				continue;
			} else if(token() ==RETURN  || token() == EOF){
				throw new IFMLException(Code.T1006,"获取变量的值时，值的\"没有成对出现");
			}else {
				break;
			}
		}
		return sb.toString();
	}

	@Override
	public String singleQuotationStringVal() {
		StringBuffer sb = new StringBuffer();
		for (;;) {
			nextToken();
			if (token() != SINGLEQUOTATION) {
				sb.append(getChar());
				continue;
			} else if(token() ==RETURN  || token() == EOF){
				throw new IFMLException(Code.T1007,"获取变量的值时，值的\'没有成对出现");
			}else {
				break;
			}
		}
		return sb.toString();
	}

	@Override
	public String[] arrayStringVal() {
		List<String> arrayStr = new ArrayList<String>();
		for (;;) {
			nextToken();
			if (token() == DOUBLEQUOTATION) {
				String str = doubleQuotationStringVal();
				arrayStr.add(str);
				continue;
			}else if(token() == SINGLEQUOTATION) {
				String str = singleQuotationStringVal();
				arrayStr.add(str);
				continue;
			} else if(token() ==COMMA) {
				continue;
			}else if(token() ==RBRACKET) {
				break;
			} 
			
			else if(token() == BLANK || token() == TABBLANK) {
				continue;
			}else {
				throw new IFMLException(Code.T1008,"去数组值，遇到了异常字符:"+getChar());
			}
		}
		String[] array = new String[arrayStr.size()];
		return  arrayStr.toArray(array);
	}
	
	@Override
	public String LbraceStringVal() {
		StringBuffer sb = new StringBuffer("{");
		char[] arrayChar = null;
		sb.append(identifierVal(arrayChar));
		sb.append("}");
		return sb.toString();
	}

}
